| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- <template>
- <div>
- <breadCrumbs
- :main-menu="breadcrumbData.mainMenu"
- :current-sub-menu="breadcrumbData.currentSubMenu"
- :sub-menu-items="breadcrumbData.subMenuItems"
- />
- <div class="ovwner--wrapper">
- <div class="title--visual">
- <h2>PROMOTION</h2>
- </div>
- <div class="detail--container">
- <div class="content--wrap">
- <div v-if="loading" class="view--wrap">
- <p style="text-align: center; padding: 40px 0;">로딩 중...</p>
- </div>
- <div v-else-if="error" class="view--wrap">
- <p style="text-align: center; padding: 40px 0; color: red;">{{ error }}</p>
- </div>
- <div v-else-if="eventData" class="view--wrap">
- <div class="title--wrap">
- <h2>{{ eventData.title }}</h2>
- <div class="meta--info">
- <p>{{ formatDate(eventData.created_at) }}</p>
- <p v-if="eventData.start_date && eventData.end_date" class="event--period">
- 기간: {{ formatDate(eventData.start_date) }} ~ {{ formatDate(eventData.end_date) }}
- </p>
- </div>
- </div>
- <div class="cont--wrap">
- <div class="suneditor-content" v-html="eventData.content"></div>
- <!-- 링크 섹션 (Iron Auto only) -->
- <div v-if="config.public.company === 'i' && eventLinks.length > 0" class="event--links">
- <div class="link--title">관련 링크</div>
- <div class="link--buttons">
- <div v-for="(link, index) in eventLinks" :key="index" class="link--item">
- <a :href="link" target="_blank" rel="noopener noreferrer" class="link--button">
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M7.33333 8.66667L12.6667 3.33333M12.6667 3.33333H9.33333M12.6667 3.33333V6.66667M6.66667 2H4C3.46957 2 2.96086 2.21071 2.58579 2.58579C2.21071 2.96086 2 3.46957 2 4V12C2 12.5304 2.21071 13.0391 2.58579 13.4142C2.96086 13.7893 3.46957 14 4 14H12C12.5304 14 13.0391 13.7893 13.4142 13.4142C13.7893 13.0391 14 12.5304 14 12V9.33333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
- </svg>
- {{ link }}
- </a>
- </div>
- </div>
- </div>
- <!-- <div v-if="fileLinks.length > 0" class="file--links">
- <div class="file--title">첨부파일</div>
- <div class="file--buttons">
- <div v-for="(link, index) in fileLinks" :key="index" class="file--item">
- <a :href="link.url" :target="link.target" class="file--button">
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M14 10v2.667A1.333 1.333 0 0 1 12.667 14H3.333A1.333 1.333 0 0 1 2 12.667V10m2.667-4L8 9.333 11.333 6M8 2v7.333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
- </svg>
- {{ link.text }}
- </a>
- <span class="file--tooltip">{{ link.title }}</span>
- </div>
- </div>
- </div> -->
- </div>
- <!-- 이전글/다음글 -->
- <div class="post--navigation">
- <div class="nav--item prev" :class="{ disabled: !eventData.prev_post }">
- <NuxtLink v-if="eventData.prev_post" :to="`${isLincolnPage == false ? '/ford' : '/lincoln'}/owner/event/${eventData.prev_post.id}`" class="nav--link">
- <span class="nav--label">이전글</span>
- <span class="nav--title">{{ eventData.prev_post.title }}</span>
- </NuxtLink>
- <div v-else class="nav--link disabled">
- <span class="nav--label">이전글</span>
- <span class="nav--title">이전글이 없습니다.</span>
- </div>
- </div>
- <div class="nav--item next" :class="{ disabled: !eventData.next_post }">
- <NuxtLink v-if="eventData.next_post" :to="`${isLincolnPage == false ? '/ford' : '/lincoln'}/owner/event/${eventData.next_post.id}`" class="nav--link">
- <span class="nav--label">다음글</span>
- <span class="nav--title">{{ eventData.next_post.title }}</span>
- </NuxtLink>
- <div v-else class="nav--link disabled">
- <span class="nav--label">다음글</span>
- <span class="nav--title">다음글이 없습니다.</span>
- </div>
- </div>
- </div>
- <div class="btn--wrap" style="margin-top: 40px; text-align: center;">
- <button
- @click="goToList"
- class="btn--primary"
- style="padding: 12px 40px; background: #000; color: #fff; border: none; cursor: pointer;"
- >
- 목록으로
- </button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- const { getMediaUrl } = useImage();
- import { ref, computed, onMounted } from 'vue';
- import { useRoute, useRouter } from 'vue-router';
- import '~/assets/scss/suneditor-content.scss';
- const route = useRoute();
- const router = useRouter();
- const config = useRuntimeConfig();
- const { apiBase } = useCompany();
- const eventData = ref(null);
- const loading = ref(true);
- const error = ref(null);
- const isLincolnPage = computed(() => {
- return route.path.includes("lincoln");
- });
- // 파일명 자르기 함수 (20자 내외)
- const truncateFileName = (fileName) => {
- if (!fileName) return '';
- // 확장자 분리
- const lastDotIndex = fileName.lastIndexOf('.');
- const name = lastDotIndex > 0 ? fileName.substring(0, lastDotIndex) : fileName;
- const ext = lastDotIndex > 0 ? fileName.substring(lastDotIndex) : '';
- // 이름이 20자 이하면 그대로 반환
- if (name.length <= 20) {
- return fileName;
- }
- // 20자로 자르고 ... 추가 후 확장자 붙이기
- return name.substring(0, 20) + '...' + ext;
- };
- // 링크 목록 생성 (Iron Auto only)
- const eventLinks = computed(() => {
- if (!eventData.value || !eventData.value.link) return [];
- try {
- const links = Array.isArray(eventData.value.link)
- ? eventData.value.link
- : JSON.parse(eventData.value.link || '[]');
- // null이나 빈 문자열 제거
- return links.filter(link => link && link.trim() !== '');
- } catch (e) {
- console.error('링크 파싱 오류:', e);
- return [];
- }
- });
- // 파일 링크 목록 생성
- const fileLinks = computed(() => {
- if (!eventData.value || !eventData.value.file_urls) return [];
- try {
- const files = Array.isArray(eventData.value.file_urls)
- ? eventData.value.file_urls
- : JSON.parse(eventData.value.file_urls || '[]');
- return files.map(file => {
- let fileUrl = file.url || file;
- const originalFileName = file.name || file;
- // 상대 경로면 도메인 붙이기
- if (fileUrl.startsWith('/')) {
- fileUrl = apiBase.value + fileUrl;
- }
- return {
- text: truncateFileName(originalFileName),
- url: fileUrl,
- target: '_blank',
- class: 'pl--0',
- title: originalFileName // 툴팁으로 전체 파일명 표시
- };
- });
- } catch (e) {
- console.error('파일 파싱 오류:', e);
- return [];
- }
- });
- // 날짜 포맷팅
- const formatDate = (dateString) => {
- if (!dateString) return '-';
- const date = new Date(dateString);
- return `${date.getFullYear()}.${String(date.getMonth() + 1).padStart(2, '0')}.${String(date.getDate()).padStart(2, '0')}`;
- };
- // Event 데이터 로드
- const loadEventData = async () => {
- try {
- loading.value = true;
- error.value = null;
- const id = route.params.id;
- const response = await fetch(`${apiBase.value}/api/event/public/${id}`);
- const result = await response.json();
- console.log('[Event Detail] API 응답:', result);
- if (!result.success) {
- error.value = result.message || '이벤트 정보를 불러올 수 없습니다.';
- console.error('[Event Detail] API 에러:', result);
- } else if (result.data) {
- eventData.value = result.data;
- console.log('[Event Detail] 로드 성공:', eventData.value);
- } else {
- error.value = '이벤트 정보를 찾을 수 없습니다.';
- }
- } catch (e) {
- console.error('[Event Detail] 로드 실패:', e);
- error.value = '이벤트 정보를 불러오는 중 오류가 발생했습니다.';
- } finally {
- loading.value = false;
- }
- };
- // 목록으로 돌아가기
- const goToList = () => {
- router.push('/ford/owner/event');
- };
- // 샘플 데이터
- const breadcrumbData = {
- mainMenu: "OWNER",
- currentSubMenu: "프로모션",
- subMenuItems: [
- { label: "프로모션", to: '/ford/owner/event', active: false },
- ],
- };
- onMounted(() => {
- loadEventData();
- });
- </script>
- <style scoped lang="scss">
- .event--links {
- margin-top: 40px;
- padding-top: 24px;
- border-top: 1px solid rgba(252, 252, 253, 0.2);
- }
- .link--title {
- font-size: 16px;
- font-weight: 600;
- color: rgba(252, 252, 253, 1);
- margin-bottom: 16px;
- }
- .link--buttons {
- display: flex;
- flex-direction: column;
- gap: 12px;
- }
- .link--item {
- display: block;
- }
- .link--button {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- padding: 12px 20px;
- background-color: rgba(255, 255, 255, 0.1);
- border: 1px solid rgba(252, 252, 253, 0.3);
- border-radius: 50px;
- color: rgba(252, 252, 253, 0.9);
- font-size: 14px;
- text-decoration: none;
- transition: all 0.3s ease;
- cursor: pointer;
- word-break: break-all;
- svg {
- flex-shrink: 0;
- }
- &:hover {
- background-color: rgba(255, 255, 255, 0.2);
- border-color: rgba(252, 252, 253, 0.5);
- transform: translateY(-2px);
- }
- &:active {
- transform: translateY(0);
- }
- }
- .file--links {
- margin-top: 40px;
- padding-top: 24px;
- border-top: 1px solid #444c57;
- }
- .file--title {
- font-size: 16px;
- font-weight: 600;
- color: rgba(252, 252, 253, 1);
- margin-bottom: 16px;
- }
- .file--buttons {
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
- }
- .file--item {
- position: relative;
- display: inline-block;
- &:hover {
- .file--tooltip {
- opacity: 1;
- visibility: visible;
- transform: translateY(-5px);
- }
- }
- }
- .file--button {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- padding: 12px 20px;
- background-color: rgba(255, 255, 255, 0.1);
- border: 1px solid rgba(252, 252, 253, 0.3);
- border-radius: 50px;
- color: rgba(252, 252, 253, 0.9);
- font-size: 14px;
- text-decoration: none;
- transition: all 0.3s ease;
- cursor: pointer;
- svg {
- flex-shrink: 0;
- }
- &:hover {
- background-color: rgba(255, 255, 255, 0.2);
- border-color: rgba(252, 252, 253, 0.5);
- transform: translateY(-2px);
- }
- &:active {
- transform: translateY(0);
- }
- }
- .file--tooltip {
- position: absolute;
- bottom: calc(100% + 10px);
- left: 50%;
- transform: translateX(-50%);
- background-color: rgba(0, 0, 0, 0.95);
- color: #fff;
- padding: 10px 14px;
- border-radius: 6px;
- font-size: 13px;
- white-space: nowrap;
- opacity: 0;
- visibility: hidden;
- transition: all 0.25s ease;
- pointer-events: none;
- z-index: 1000;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
- &::after {
- content: '';
- position: absolute;
- top: 100%;
- left: 50%;
- transform: translateX(-50%);
- border: 7px solid transparent;
- border-top-color: rgba(0, 0, 0, 0.95);
- }
- }
- .file--item:hover .file--tooltip {
- transform: translateX(-50%) translateY(-5px);
- }
- </style>
|